package edu.colorado.cs.correll.cudroplet.shaperecognition;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import processing.core.PApplet;

import com.vividsolutions.jts.algorithm.Angle;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;

public class AlphaShape {
	private static final float DELTA = 10;
	ShapeRecognition shapeRecognition;
	Set<Circumcircle> circumcircles;
	Robot max;
	CircumcircleDictionary circumcircleDictionary;

	public AlphaShape(ShapeRecognition shapeRecognition,
			Set<Circumcircle> circumcircles, Leader leader) {
		this.shapeRecognition = shapeRecognition;
		this.circumcircles = circumcircles;

		circumcircleDictionary = new CircumcircleDictionary(circumcircles);

		max = RobotUtils.getMax(circumcircleDictionary.robotMap.keySet());
		leader.setLeader(max.id);
	}

	public Coordinate[] getCoordinates() {
		return getCenteredCoordinates(new Coordinate(0, 0));
	}

	public Coordinate[] getCenteredCoordinates(Coordinate centroid) {
		Set<Coordinate> coordinates = new HashSet<Coordinate>();

		for (Circumcircle circumcircle : circumcircles) {
			coordinates.add(circumcircle.a.getCoordinate());
			coordinates.add(circumcircle.b.getCoordinate());
		}
		int i = 0;
		Coordinate[] coords = new Coordinate[coordinates.size()];
		for (Coordinate coord : coordinates) {
			coord.x -= centroid.x;
			coord.y -= centroid.y;
			coords[i++] = coord;
		}
		return coords;
	}

	public List<Circumcircle> getShell() {
		// get the leftmost starting point of all circumcircles
		// This is guaranteed to be on the shell
		Robot leftmostRobot = null;
		for (Robot robot: circumcircleDictionary.circumcircleMap.keySet()) {
			if (leftmostRobot == null || leftmostRobot.x > robot.x) {
				leftmostRobot = robot;
			}
		}
		Coordinate prevCoord = new Coordinate(leftmostRobot.x, leftmostRobot.y - DELTA);
		Robot curr = leftmostRobot;
		List<Circumcircle> shell = new ArrayList<Circumcircle>();
		do {
					Set<Circumcircle> cs = circumcircleDictionary.circumcircleMap
							.get(curr);
					double maxInteriorAngle = PApplet.MIN_FLOAT;
					Circumcircle argMax = null;
					for(Circumcircle circumcircle: cs) {
						double interiorAngle;
						if(prevCoord.equals(circumcircle.b.getCoordinate())) {
							interiorAngle = 0;
						} else {
							// the ring is assumed to be oriented in a clockwise direction
							interiorAngle = Angle.interiorAngle(circumcircle.b.getCoordinate(), 
								curr.getCoordinate(), prevCoord);
						}
						if(interiorAngle > maxInteriorAngle) {
							maxInteriorAngle = interiorAngle;
							argMax = circumcircle;
						}
					}
					prevCoord = curr.getCoordinate();
					curr = argMax.b;
					shell.add(argMax);
					if(shell.size() > circumcircles.size()) {
						PApplet.println("bug, max=" + max.id);
						break;
					}
			
		}
		while (!leftmostRobot.equals(curr));
		return shell;
	}
	
	public static Coordinate[] getLinearRingCoordinates(List<Circumcircle> shell) {
		Coordinate[] points = new Coordinate[shell.size()+1];
		int i = 0;
		for(Circumcircle circumcircle: shell) {
			points[i++] = circumcircle.a.getCoordinate();
		}
		points[i] = shell.get(0).a.getCoordinate();
		return points;
	}
	
	public static Coordinate[][] getCenteredLines(List<Circumcircle> shell, 
			Coordinate centroid) {
		Coordinate[][] lines = new Coordinate[shell.size()][2];
		int i = 0;
		for (Circumcircle circumcircle : shell) {
			Coordinate[] coords = { circumcircle.a.getCoordinate(),
					circumcircle.b.getCoordinate() };
			for (Coordinate coord : coords) {
				coord.x -= centroid.x;
				coord.y -= centroid.y;
			}
			lines[i++] = coords;
		}
		return lines;
	}
	
	public Geometry getGeometry() {
		return getCenteredGeometry(new Coordinate(0,0));
	}

	public Geometry getCenteredGeometry(Coordinate centroid) {
		List<Circumcircle> shell = getShell();
		Geometry geometry;
		if(shell.size() > 2) {
			Coordinate[] coordinates = AlphaShape.getLinearRingCoordinates(shell);
			for(Coordinate coordinate: coordinates) {
				coordinate.x -= centroid.x;
				coordinate.y -= centroid.y;
			}
			geometry = shapeRecognition.geometryFactory.createPolygon(shapeRecognition.geometryFactory.createLinearRing(coordinates), null);
		} else {
			Circumcircle circumcircle = shell.get(0);
			Coordinate[] coordinates = {circumcircle.a.getCoordinate(), circumcircle.b.getCoordinate()};
			for(Coordinate coordinate: coordinates) {
				coordinate.x -= centroid.x;
				coordinate.y -= centroid.y;
			}
			geometry = shapeRecognition.geometryFactory.createLineString(coordinates);
		}
		return geometry;
	}
}
